using Crux.Core.Payloads;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;

namespace Crux.Core.Mvc.Filters;
/// <summary>
/// 
/// </summary>
public interface IExceptionHandler
{
    /// <summary>
    /// 
    /// </summary>
    void Next(ExceptionContext context);
}
/// <inheritdoc/>
public class BadRequestExceptionHandler : IExceptionHandler
{
    private readonly IExceptionHandler handler;
    /// <inheritdoc/>
    public BadRequestExceptionHandler(IExceptionHandler handler)
    {
        this.handler = handler ?? throw new ArgumentNullException(nameof(handler));
    }
    /// <inheritdoc/>
    public void Next(ExceptionContext context)
    {
        if (typeof(BadRequestException) != context.Exception.GetType())
        {
            this.handler.Next(context);
            return;
        }
        context.ExceptionHandled = true;
        context.HttpContext.Response.StatusCode = 400;
        context.Result = new JsonResult(Payload.Error(context.Exception.Message));
    }
}
/// <inheritdoc/>
public class ResourceNotFoundExceptionHandler : IExceptionHandler
{
    private readonly IExceptionHandler handler;
    /// <inheritdoc/>
    public ResourceNotFoundExceptionHandler(IExceptionHandler handler)
    {
        this.handler = handler ?? throw new ArgumentNullException(nameof(handler));
    }
    /// <inheritdoc/>
    public void Next(ExceptionContext context)
    {
        if (typeof(ResourceNotFoundException) != context.Exception.GetType())
        {
            this.handler.Next(context);
            return;
        }
        context.ExceptionHandled = true;
        context.HttpContext.Response.StatusCode = 404;
        context.Result = new JsonResult(Payload.NotFound(context.Exception.Message));
    }
}
/// <inheritdoc/>
public class UnauthorizedExceptionHandler : IExceptionHandler
{
    private readonly IExceptionHandler handler;
    /// <inheritdoc/>
    public UnauthorizedExceptionHandler(IExceptionHandler handler)
    {
        this.handler = handler ?? throw new ArgumentNullException(nameof(handler));
    }
    /// <inheritdoc/>
    public void Next(ExceptionContext context)
    {
        if (typeof(UnauthorizedException) != context.Exception.GetType())
        {
            this.handler.Next(context);
            return;
        }
        context.ExceptionHandled = true;
        context.HttpContext.Response.StatusCode = 401;
        context.Result = new JsonResult(Payload.Unauthorized(context.Exception.Message));
    }
}
/// <inheritdoc/>
public class ForbiddenExceptionHandler : IExceptionHandler
{
    private readonly IExceptionHandler handler;
    /// <inheritdoc/>
    public ForbiddenExceptionHandler(IExceptionHandler handler)
    {
        this.handler = handler ?? throw new ArgumentNullException(nameof(handler));
    }
    /// <inheritdoc/>
    public void Next(ExceptionContext context)
    {
        if (typeof(ForbiddenException) != context.Exception.GetType())
        {
            this.handler.Next(context);
            return;
        }
        context.ExceptionHandled = true;
        context.HttpContext.Response.StatusCode = 403;
        context.Result = new JsonResult(Payload.Forbidden(context.Exception.Message));
    }
}
/// <inheritdoc/>
public class ConflictExceptionHandler : IExceptionHandler
{
    private readonly IExceptionHandler handler;
    /// <inheritdoc/>
    public ConflictExceptionHandler(IExceptionHandler handler)
    {
        this.handler = handler ?? throw new ArgumentNullException(nameof(handler));
    }
    /// <inheritdoc/>
    public void Next(ExceptionContext context)
    {
        if (typeof(ConflictException) != context.Exception.GetType())
        {
            this.handler.Next(context);
            return;
        }

        context.ExceptionHandled = true;
        context.HttpContext.Response.StatusCode = 409;
        context.Result = new JsonResult(Payload.Conflict(context.Exception.Message));
    }
}
/// <inheritdoc/>
public class LockedExceptionHandler : IExceptionHandler
{
    private readonly IExceptionHandler handler;
    /// <inheritdoc/>
    public LockedExceptionHandler(IExceptionHandler handler)
    {
        this.handler = handler ?? throw new ArgumentNullException(nameof(handler));
    }
    /// <inheritdoc/>
    public void Next(ExceptionContext context)
    {
        if (typeof(LockedException) != context.Exception.GetType())
        {
            this.handler.Next(context);
            return;
        }
        context.ExceptionHandled = true;
        context.HttpContext.Response.StatusCode = 423;
        context.Result = new JsonResult(Payload.Conflict(context.Exception.Message));
    }
}
/// <inheritdoc/>
public class OtherExceptionHandler : IExceptionHandler
{
    private readonly ILogger logger;
    /// <inheritdoc/>
    public OtherExceptionHandler(
        ILogger logger)
    {
        this.logger = logger
            ?? throw new ArgumentNullException(nameof(logger));
    }
    /// <inheritdoc/>
    public void Next(ExceptionContext context)
    {
        context.HttpContext.Response.StatusCode = 500;
        context.Result = new JsonResult(Payload.InternalServerError(context.Exception.StackTrace));
        context.ExceptionHandled = true;
        this.logger.LogError(context.Exception, context.Exception.Message);
    }
}